
#include "BBox.h"
#include "Vertex.h"
#include "Engine.h"
#include "Camera.h"
#include <string>
#include <d3dx9math.h>

//
// Constructor
//

BBox::BBox()
{
	Clear();
}

BBox::BBox(const Real3 &_point, const Vector3 &_extent)
{
	point = _point;
	extent = _extent;
}

BBox::BBox(const BBox &box, const D3DXMATRIX *rotation)
{
	Clear();
	
	Real3 p[] = 
	{
		box.point,
		box.point + box.extent,

		box.point + Real3(box.extent.x, box.extent.y, 0.0f        ),
		box.point + Real3(box.extent.x, 0.0f,         box.extent.z),

		box.point + Real3(0.0f,         box.extent.y, box.extent.z),
		box.point + Real3(box.extent.x, 0.0f,         0.0f        ),

		box.point + Real3(0.0f,         box.extent.y, 0.0f        ),
		box.point + Real3(0.0f,         0.0f,         box.extent.z)
	};

	D3DXVec3TransformCoordArray((D3DXVECTOR3*)p, sizeof(Real3), (D3DXVECTOR3*)p, sizeof(Real3), rotation, 8);
	for(uint i = 0; i < 8; ++i)
		AddPoint(p[i]);
}

#define _max(x,y,z) max(max(x,y),z)
#define _min(x,y,z) min(min(x,y),z)

BBox::BBox(const Real3 &a, const Real3 &b, const Real3 &c)
{
	Real3 minP(_min(a.x, b.x, c.x), _min(a.y, b.y, c.y), _min(a.z, b.z, c.z));
	Real3 maxP(_max(a.x, b.x, c.x), _max(a.y, b.y, c.y), _max(a.z, b.z, c.z));

	point = minP;
	extent = maxP - minP;
}

//
// Operators
//

BBox operator + (const BBox &a, const BBox &b)
{
	return BBox(Real3(min(a.point.x, b.point.x),
		                min(a.point.y, b.point.y),
						     	  min(a.point.z, b.point.z)),
							Real3(max(a.extent.x, b.extent.x),
		                max(a.extent.y, b.extent.y),
						     	  max(a.extent.z, b.extent.z)));
}

BBox BBox::operator += (const BBox &a)
{ 
	point.x = min(point.x, a.point.x);
	point.y = min(point.y, a.point.y);
	point.z = min(point.z, a.point.z);
	
	extent.x = max(extent.x, a.extent.x);
	extent.y = max(extent.y, a.extent.y);
	extent.z = max(extent.z, a.extent.z);

	return *this;
}

BBox operator + (const BBox &a, const Real3 &b)
{
	return BBox(a.point + b, a.extent);
}

BBox operator - (const BBox &a, const Real3 &b)
{
	return BBox(a.point - b, a.extent);
}

BBox BBox::operator += (const Real3 &a)
{ 
	point += a;

	return *this;
}

BBox BBox::operator -= (const Real3 &a)
{ 
	point -= a;

	return *this;
}

//
// Add Point
//

void BBox::AddPoint(const Real3 &p)
{
	extent.x = max(extent.x, p.x - point.x);
	extent.y = max(extent.y, p.y - point.y);
	extent.z = max(extent.z, p.z - point.z);

	if(p.x < point.x)
	{
		extent.x += point.x - p.x;
		point.x = p.x;
	}
	if(p.y < point.y)
	{
		extent.y += point.y - p.y;
		point.y = p.y;
	}
	if(p.z < point.z)
	{
		extent.z += point.z - p.z;
		point.z = p.z;
	}
}

//
// IsGood
//

bool BBox::IsGood()
{ 
	return extent.x > 0 && extent.y > 0 && extent.z > 0;
}

//
// Centralize
//

Real3 BBox::Centralize()
{
	Real3 c = Center();
	point -= c;
	return c;
}

//
// CalculateVertexPoints
//

void BBox::CalculateVertexPoints(vector<Real3> &ptr, const Real3 &pos)
{
	if(DistanceBetweenSq(pos + point, engine.camera->Position()) > 100)
		return;

	ptr.push_back(point + Real3(    0.0f,     0.0f, extent.z) + pos);
	ptr.push_back(point + Real3(    0.0f, extent.y, extent.z) + pos);
	ptr.push_back(point + Real3(extent.x, extent.y, extent.z) + pos);
	ptr.push_back(point + Real3(extent.x,     0.0f, extent.z) + pos);
	
	ptr.push_back(point + Real3(    0.0f,     0.0f,     0.0f) + pos);
	ptr.push_back(point + Real3(    0.0f, extent.y,     0.0f) + pos);
	ptr.push_back(point + Real3(extent.x, extent.y,     0.0f) + pos);
	ptr.push_back(point + Real3(extent.x,     0.0f,     0.0f) + pos);

	/*ptr.push_back(point + Real3(    0.0f,     0.0f, extent.z));
	ptr.push_back(point + Real3(    0.0f, extent.y, extent.z));
	ptr.push_back(point + Real3(extent.x, extent.y, extent.z));
	ptr.push_back(point + Real3(extent.x,     0.0f, extent.z));
	
	ptr.push_back(point + Real3(    0.0f,     0.0f,     0.0f));
	ptr.push_back(point + Real3(    0.0f, extent.y,     0.0f));
	ptr.push_back(point + Real3(extent.x, extent.y,     0.0f));
	ptr.push_back(point + Real3(extent.x,     0.0f,     0.0f));*/
}

//
// Clear
//

void BBox::Clear()
{
	point.Set(  INFINITY,  INFINITY,  INFINITY);
	extent.Set(-INFINITY, -INFINITY, -INFINITY);
}

//
// Collides
//

bool BBoxCollides(const BBox &a, const BBox &b)
{
	Real3 tmin = a.point;
	Real3 tmax = a.point + a.extent;
	Real3 bmin = b.point;
	Real3 bmax = b.point + b.extent;

	return ((tmax.x >= bmin.x && bmin.x >= tmin.x)  || (tmin.x <= bmax.x && bmax.x <= tmax.x) || (tmin.x >= bmin.x && tmax.x <= bmax.x)) &&
			   ((tmax.y >= bmin.y && bmin.y >= tmin.y)  || (tmin.y <= bmax.y && bmax.y <= tmax.y) || (tmin.y >= bmin.y && tmax.y <= bmax.y)) &&
			   ((tmax.z >= bmin.z && bmin.z >= tmin.z)  || (tmin.z <= bmax.z && bmax.z <= tmax.z) || (tmin.z >= bmin.z && tmax.z <= bmax.z));
}

bool BBoxCollides(const BBox &a, const D3DXMATRIX *aRot, const BBox &b, const D3DXMATRIX *bRot)
{
	return BBoxCollides(BBox(a, aRot), BBox(b, bRot));
}